import { buffer, stream } from "@kit.ArkTS";
import hilog from "@ohos.hilog";
import { PassThrough } from "./passThrough";


/**
 * @internal
 * @param stream - to be read.
 * @param bytes - read head bytes from the stream and discard the rest of it.
 *
 * Caution: the input stream must be destroyed separately, this function does not do so.
 */
export const headStream = (inputStream: stream.Readable, bytes: number): Promise<Uint8Array> => {
  const TAG = 'headStream'
  return new Promise((resolve, reject) => {
    const chunks: Uint8Array[] = [];
    let bytesBuffered = 0;
    inputStream.on('data', (data) => {
      hilog.info(0, TAG, 'on data ' + JSON.stringify(data))
      if (bytesBuffered<bytes){
        if (typeof data.data === 'string') {
          const chunk = stringToBuffer(data.data)
          chunks.push(chunk)
          bytesBuffered += chunk.byteLength;
        }
        if (data.data instanceof Uint8Array) {
          chunks.push(data.data);
          bytesBuffered += data.data.byteLength;
        }
        if (bytesBuffered > bytes) {
          const excess = bytesBuffered - bytes;
          const tailBuffer = chunks[chunks.length - 1];
          chunks[chunks.length - 1] = tailBuffer.subarray(0, tailBuffer.byteLength - excess);
        }
      }
    });

    inputStream.on('end', () => {
      hilog.info(0, TAG, 'on end chunk length = '+chunks.length)
      if (chunks.length>0) {
        resolve(new Uint8Array(buffer.concat(chunks).buffer));
      }else{
        resolve(new Uint8Array([]));
      }
    });

    inputStream.on('error', (err) => {
      hilog.error(0, TAG, 'on error '+JSON.stringify(err))
      reject(err);
    });
    
    if (inputStream instanceof PassThrough) {
      inputStream.onEndCheckError((err)=>{
        hilog.error(0, TAG, 'on end check error '+JSON.stringify(err))
        reject(err);
      })
    }
    inputStream.pipe(new Collector())
  });
};

const stringToBuffer = (input: string, encoding?: string) => {
  if (encoding && buffer.isEncoding(encoding)) {
    return new Uint8Array(buffer.from(input, encoding as buffer.BufferEncoding).buffer)
  } else {
    return new Uint8Array(buffer.from(input).buffer)
  }
}

class Collector extends stream.Writable {
  doWrite(chunk: string | Uint8Array, encoding: string, callback: Function): void {
    callback();
  }
}
